﻿//  Copyright (c) 2011 Ray Liang (http://www.dotnetage.com)
//  Licensed MIT: http://www.opensource.org/licenses/mit-license.php

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DNA.Mvc.jQuery;
using System.Web;
using System.Web.Security;

namespace DNA.Mvc.UI
{
    public class SiteMapNodeProvider : IHierarchicalNodeProvider
    {
        private WebSiteContext Context;

        public SiteMapNodeProvider(WebSiteContext context)
        {
            Context = context;
        }

        public void AddChildren(HierarchicalNode parentNode, HierarchicalNode node) { }

        public HierarchicalNode CurrentNode
        {
            get
            {
                var _node = Context.Page;
                if (_node != null)
                    return PopuplateNavigatableNode(_node);
                return null;
            }
        }

        public bool EnableLocalization
        {
            get { return false; }
        }

        public HierarchicalNode FindNodeFormKey(string key)
        {
            return PopuplateNavigatableNode(GetPageFromKey(GetIDFromKey(key)));
        }

        public IEnumerable<HierarchicalNode> GetChildNodes(HierarchicalNode node)
        {
            if (node == null)
                throw new ArgumentNullException("node");
            var web = RootNode.Item as Web;
            int parentID = GetIDFromKey(node.Key);
            return PopuplateNavigatableNodes(web.WebPages.OrderBy(p=>p.Pos).Where(p => p.ParentID == parentID));
        }

        public string[] GetNodeRoles(HierarchicalNode node)
        {
            if (node == null)
                throw new ArgumentNullException("node");

            var page = node.Item as WebPage;

            if (page == null)
                throw new ArgumentNullException("Node.Item");

            return page.Roles.Select(r => r.RoleName).ToArray();
        }

        public HierarchicalNode GetParentNode(HierarchicalNode node)
        {
            if (node == null)
                throw new ArgumentNullException("node");
            int id = GetIDFromKey(node.Key);
            if (id == 0)
                return null;
            else
            {
                var page = GetPageFromKey(GetIDFromKey(node.Key));
                if (page.ParentID == 0)
                    return RootNode;
                else
                {
                    return PopuplateNavigatableNode(this.GetPageFromKey(page.ParentID));
                }
            }
        }

        private Dictionary<int, string[]> roleTable = null;
        
        private void PreloadRoleTable()
        {
            int[] pIDs = Context.Web.WebPages.Where(p=>!p.AllowAnonymous).Select(p => p.ID).ToArray();
            var roles = Context.DataContext.WebPages.GetRoles(pIDs);
            roleTable = new Dictionary<int, string[]>();
            var roleGroups= roles.GroupBy(r => r.PageID);
            foreach (var roleGroup in roleGroups)
            {
                roleTable.Add(roleGroup.Key, roleGroup.Select(r => r.RoleName).ToArray());
            }
        }

        public bool IsAccessibleToUser(System.Web.HttpContext context, HierarchicalNode node)
        {
            if (node.Equals(RootNode)) return true;
            var page = GetPageFromKey(GetIDFromKey(node.Key));

            if (page.AllowAnonymous)
                return true;

            if (context.Request.IsAuthenticated)
            {
                if (roleTable == null)
                    PreloadRoleTable();

                if (roleTable.ContainsKey(page.ID))
                {
                    var roles = roleTable[page.ID];
                    foreach (var role in roles)
                    {
                        if (Roles.IsUserInRole(role))
                            return true;
                    }
                }
            }
            return false;
        }

        public void RemoveNode(HierarchicalNode node) { }

        public string ResourceKey
        {
            get { throw new NotImplementedException(); }
        }

        private HierarchicalNode rootNode;

        public HierarchicalNode RootNode
        {
            get
            {
                var web = Context.Web;
                if (web == null)
                    web = Context.RootWeb;

                if ((rootNode == null) || (rootNode.Title != web.Title))
                {
                    rootNode = new HierarchicalNode(this)
                    {
                        Title = web.Title,
                        Description = web.Description,
                        NavigateUrl = VirtualPathUtility.ToAbsolute("~/"),
                        Key = "0",
                        Item = web
                    };
                    rootNode.Attributes.Add("key", 0);
                }
                rootNode.ParentNode = null;
                return rootNode;
            }
        }

        private IEnumerable<HierarchicalNode> PopuplateNavigatableNodes(IEnumerable<WebPage> pages)
        {
            List<HierarchicalNode> nodes = new List<HierarchicalNode>();
            foreach (var page in pages)
                nodes.Add(PopuplateNavigatableNode(page));
            return nodes;
        }

        private HierarchicalNode PopuplateNavigatableNode(WebPage page)
        {
            if (page == null)
                throw new ArgumentNullException("Page");

            var node = new HierarchicalNode(this)
            {
                Title = page.Title,
                Description = page.Description,
                NavigateUrl = VirtualPathUtility.ToAbsolute(page.Path),
                Key = page.ID.ToString(),
                ImageUrl = page.IconUrl
            };
            node.Target = page.Target;
            node.Attributes.Add("key", page.ID.ToString());
            node.Attributes.Add("ShowInMenu", page.ShowInMenu);
            node.Item = page;
            return node;
        }

        private WebPage GetPageFromKey(int key)
        {
            return Context.Web.WebPages.First(p => p.ID == key);
        }

        private int GetIDFromKey(string key)
        {
            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException("Key is empty");
            int id = 0;
            bool valid = int.TryParse(key, out id);
            return id;
        }
    }
}
